"""
@Time ： 2025/9/9 14:36
@Auth ： zh
@File ：get_fbfx_price.py
"""
# 用于执行匹配1.41 分部分项费占比计算 清单合价计算
import requests
import json
import sys
from typing import List, Dict, Union

# -------------------------- 1. 配置基础参数 --------------------------
# 第一步接口：获取structureId列表（请替换为实际请求URL）
FIRST_API_URL = "https://test.icii.agtsci.cn/api/cis/quota/projectStructure/tree"
# 第二步接口：获取清单数据
SECOND_API_URL = "https://test.icii.agtsci.cn/api/cis/quota/collect/bill/billTree"
# 第三步接口：获取分部分项费数据
THIRD_API_URL = "https://test.icii.agtsci.cn/api/cis/quota/result/investmentCost/specialtyCostDistributionInfo"
# 目标清单编码片段（需“包含”这些片段的billCode都纳入统计）
TARGET_BILL_CODE_FRAGMENTS = [
    *[f"011702{str(i).zfill(3)}" for i in range(1, 33)],  # 011702001~011702032
    "穗011702033",
    "011704"# 特殊编码片段
]


# -------------------------- 2. 工具函数定义 --------------------------
def get_quota_id_dynamically() -> str:
    """动态获取quotaId，支持命令行参数或手动输入，含基础校验"""
    # 1. 优先读取命令行参数（如：python script.py 7361649817522667520）
    if len(sys.argv) >= 2:
        quota_id = sys.argv[1].strip()
        if quota_id and 10 <= len(quota_id) <= 30 and quota_id.isdigit():
            print(f"从命令行获取到quotaId：{quota_id}")
            return quota_id
        else:
            print("命令行传入的quotaId格式无效，切换为手动输入")

    # 2. 手动输入模式（含校验和确认）
    while True:
        quota_id = input("请输入quotaId（通常为10-30位数字）：").strip()
        if not quota_id:
            print("错误：quotaId不能为空，请重新输入！")
            continue
        if not quota_id.isdigit():
            print("错误：quotaId应为纯数字，请重新输入！")
            continue
        if len(quota_id) < 10 or len(quota_id) > 30:
            print("错误：quotaId长度应在10-30位之间，请重新输入！")
            continue
        confirm = input(f"您输入的quotaId为：{quota_id}，是否确认？（y/n）：").strip().lower()
        if confirm == "y":
            return quota_id
        print("请重新输入quotaId...")


def send_post_request(url: str, params: Dict) -> Union[Dict, None]:
    """发送POST请求，处理超时、状态码等异常，返回JSON响应"""
    headers = {
        "Content-Type": "application/json",  # 声明请求体为JSON格式
        # 认证信息，实际使用中需替换为有效的token
        "Authorization": "bearer d2b663b6-5195-4efe-b7c9-d5d14e937b6c"
    }
    try:
        response = requests.post(
            url=url,
            data=json.dumps(params),
            headers=headers,
            timeout=15
        )
        response.raise_for_status()
        result = response.json()

        if result.get("code") == 200:
            print(f"✅ 请求成功：{url}")
            return result
        else:
            print(f"❌ 接口返回失败：{result.get('message', '未知错误')}")
            return None

    except requests.exceptions.RequestException as e:
        print(f"❌ 请求异常：{str(e)}")
        return None


def extract_structure_ids(first_api_response: Dict) -> List[str]:
    """从第一步接口响应中提取最内层children的id（structureId）"""
    structure_ids = []
    root_children = first_api_response.get("data", {}).get("children", [])
    for level1_child in root_children:
        level2_children = level1_child.get("children", [])
        for level2_child in level2_children:
            structure_id = level2_child.get("id")
            if structure_id:
                structure_ids.append(str(structure_id))
    return structure_ids


def filter_target_bill_total_costs(second_api_response: Dict) -> List[float]:
    """
    从第二步接口响应中筛选目标清单合价（包含匹配逻辑）：
    只要billCode包含任一TARGET_BILL_CODE_FRAGMENTS片段，就纳入统计
    """
    target_total_costs = []

    # 递归遍历所有层级的清单节点
    def traverse_children(children: List[Dict]):
        if not children:
            return
        for child in children:
            # 仅处理实际清单节点（billNode=true）
            if child.get("billNode") is True:
                bill_code = child.get("billCode", "")  # 默认为空字符串，避免None报错
                total_cost_str = child.get("totalCost")

                # 核心：包含匹配（判断billCode是否包含任一目标片段）
                is_target = any(fragment in bill_code for fragment in TARGET_BILL_CODE_FRAGMENTS)
                if is_target and total_cost_str:
                    try:
                        total_cost = float(total_cost_str)
                        target_total_costs.append(total_cost)
                        # 输出匹配详情（标注匹配的片段）
                        matched_fragments = [f for f in TARGET_BILL_CODE_FRAGMENTS if f in bill_code]
                        print(
                            f"📊 匹配清单：{bill_code:20} | 匹配片段：{','.join(matched_fragments):15} | 合价：{total_cost:10.2f} 元")
                    except ValueError:
                        print(f"⚠️  合价格式错误：{total_cost_str}（清单编码：{bill_code}）")

            # 递归处理子节点
            traverse_children(child.get("children", []))

    # 从data字段开始遍历
    data_list = second_api_response.get("data", [])
    for data_item in data_list:
        traverse_children(data_item.get("children", []))

    return target_total_costs


def get_subproject_cost(third_api_response: Dict) -> float:
    """从第三步接口响应中提取分部分项费的totalCost"""
    cost_composition = third_api_response.get("data", {}).get("costCompositionDistribution", [])
    for item in cost_composition:
        if item.get("name") == "分部分项费":
            return float(item.get("totalCost", 0))
    print("⚠️  未找到分部分项费数据")
    return 0.0


# -------------------------- 3. 主逻辑执行 --------------------------
if __name__ == "__main__":
    print("=" * 80)
    print("          清单合价统计工具（动态quotaId + 包含匹配逻辑）          ")
    print("=" * 80)

    # 步骤0：动态获取quotaId
    print("\n【步骤0：获取quotaId】")
    QUOTA_ID = get_quota_id_dynamically()
    print(f"✅ 已确认有效quotaId：{QUOTA_ID}")

    # 步骤1：获取所有structureId
    print("\n" + "=" * 80)
    print("【步骤1：获取structureId列表】")
    first_request_params = {"quotaId": QUOTA_ID}
    first_response = send_post_request(FIRST_API_URL, first_request_params)
    if not first_response:
        print("❌ 步骤1执行失败，终止程序")
        exit()
    structure_id_list = extract_structure_ids(first_response)
    print(f"\n✅ 步骤1执行完成，共提取{len(structure_id_list)}个structureId：")
    for idx, sid in enumerate(structure_id_list, 1):
        print(f"  {idx:2d}. {sid}")

    # 步骤2：批量请求清单接口，筛选目标合价（包含匹配）
    print("\n" + "=" * 80)
    print("【步骤2：批量请求清单接口，包含匹配目标合价】")
    print(f"📌 目标匹配规则：billCode包含以下任一片段 → {TARGET_BILL_CODE_FRAGMENTS}")
    all_target_total_costs = []

    for sid in structure_id_list:
        print(f"\n--- 正在处理 structureId：{sid} ---")
        second_request_params = {
            "quotaId": QUOTA_ID,
            "structureId": sid,
            "queryType": 0
        }
        second_response = send_post_request(SECOND_API_URL, second_request_params)
        if not second_response:
            print(f"⚠️  跳过该structureId：{sid}（请求失败）")
            continue
        current_costs = filter_target_bill_total_costs(second_response)
        all_target_total_costs.extend(current_costs)

    # 步骤3：统计合价总和
    print("\n" + "=" * 80)
    print("【步骤3：统计目标清单合价总和】")
    total_sum = sum(all_target_total_costs)

    # 格式化输出结果
    print(f"\n📋【最终统计结果】")
    print(f"1. 匹配到的目标清单合价列表（元）：")
    if all_target_total_costs:
        for idx, cost in enumerate(all_target_total_costs, 1):
            print(f"    {idx:2d}. {cost:15.2f}")
    else:
        print(f"    ⚠️  未匹配到任何清单（目标片段：{TARGET_BILL_CODE_FRAGMENTS}）")
    print(f"2. 目标清单合价总和（元）：{total_sum:20.2f}")
    print(f"3. 匹配到的清单数量：{len(all_target_total_costs)} 个")

    # 新增步骤：获取分部分项费并计算总和
    print("\n" + "=" * 80)
    print("【步骤4：获取分部分项费并计算总费用】")
    third_request_params = {
        "quotaId": QUOTA_ID,
        "structureIds": structure_id_list
    }
    third_response = send_post_request(THIRD_API_URL, third_request_params)
    if not third_response:
        print("❌ 步骤4执行失败，无法获取分部分项费数据")
    else:
        subproject_cost = get_subproject_cost(third_response)
        total_subproject_cost = subproject_cost + total_sum
        print(f"📊 分部分项费（接口返回）：{subproject_cost:20.2f} 元")
        print(f"📊 目标清单合价总和：{total_sum:20.2f} 元")
        print(f"📊 最终分部分项费总计：{total_subproject_cost:18.2f} 元")

    print("\n" + "=" * 80)
    print("🎉 所有步骤执行完成！")